package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	socketio "github.com/googollee/go-socket.io"
	"log"
	"net/http"
	"net/url"
	"strings"
)

func main() {
	router := gin.New()
	//初始化server
	server := socketio.NewServer(nil)
	//redis适配器
	ok, err := server.Adapter(&socketio.RedisAdapterOptions{
		Addr:    "127.0.0.1:6379",
		Prefix:  "socket.io",
		Network: "tcp",
	})
	if err != nil || !ok {
		log.Fatal("socket-io adapter error:", err)
	}
	// 建立连接
	server.OnConnect("/", func(s socketio.Conn) error {
		//通过query参数获取建立连接的uid
		params, _ := url.ParseQuery(s.URL().RawQuery)
		uid := params.Get("uid")
		s.SetContext(uid)
		// 加入一个房间
		s.Join("test")
		log.Println("建立连接:", s.ID(), uid) //记录连接错误信息
		return nil
	})

	// 连接错误
	server.OnError("/", func(s socketio.Conn, e error) {
		log.Println("连接错误:", s.ID(), e) //记录连接错误信息

	})

	// 关闭连接
	server.OnDisconnect("/", func(s socketio.Conn, reason string) {
		s.LeaveAll() //将socket从所有加入的room中移除
		if uid := s.Context(); uid != nil {
			log.Printf("用户[%s]断开连接", uid)
		}
		log.Println("关闭连接：", s.ID(), reason)
	})

	// ”notice“事件
	server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
		log.Println("notice收到内容：:", msg)
		s.Emit("notice", "have "+msg) // 向client回复内容
	})

	router.GET("/bcast", func(context *gin.Context) {
		// 向房间内的所有人员发消息
		server.BroadcastToRoom("/", "test", "notice", "广播通知")
	})

	//----------------------------------end----------------------------------

	go server.Serve()
	defer server.Close()
	router.Use(gin.Recovery(), Cors())
	router.GET("/socket.io/*any", gin.WrapH(server))
	router.POST("/socket.io/*any", gin.WrapH(server))
	router.StaticFS("/public", http.Dir("../asset"))

	log.Println("Serving at localhost:8000...")
	if err := router.Run(":8000"); err != nil {
		log.Fatal("failed run app: ", err)
	}
}

func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method               // 请求方法
		origin := c.Request.Header.Get("Origin") // 请求头部
		var headerKeys []string                  // 声明请求头keys
		for k, _ := range c.Request.Header {
			headerKeys = append(headerKeys, k)
		}
		headerStr := strings.Join(headerKeys, ", ")
		if headerStr != "" {
			headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
		} else {
			headerStr = "access-control-allow-origin, access-control-allow-headers"
		}
		if origin != "" {
			c.Header("Access-Control-Allow-Origin", origin)                                    // 这是允许访问所有域
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") // 服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
			//  header的类型
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
			//              允许跨域设置                                                                                                      可以返回其他子段
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
			c.Header("Access-Control-Max-Age", "172800")                                                                                                                                                           // 缓存请求信息 单位为秒
			c.Header("Access-Control-Allow-Credentials", "true")                                                                                                                                                   //  跨域请求是否需要带cookie信息 默认设置为true
			c.Set("content-type", "application/json")                                                                                                                                                              // 设置返回格式是json
		}

		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.JSON(http.StatusOK, "Options Request!")
		}
		// 处理请求
		c.Next() //  处理请求
	}
}
